package com.one.blocks.mvc.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.alibaba.excel.read.listener.ReadListener;
import com.one.blocks.mvc.enums.RepeatStrategyEnum;
import com.one.blocks.mvc.manager.BatchManager;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author linfuxin Created on 2022-07-12 13:37:11
 */
public abstract class AbstractDataListener<T, E, Convert extends ExcelConvert<T, E>, Manager extends BatchManager<T> & ExcelManager<T>>
        implements ReadListener<E> {

    protected final Manager manager;
    protected final Convert convert;

    protected List<T> cachedUpdateDataList;
    private final boolean noRepeatCheck;
    private final boolean ignoreIfRepeat;
    private final boolean overrideIfRepeat;
    private final boolean breakOffIfRepeat;
    private final T param;
    private final List<Pair<Field, Field>> fieldPairList;

    protected final UploadResult uploadResult;

    public AbstractDataListener(Manager manager, Convert convert, UploadParam uploadParam, UploadResult uploadResult, Class<T> tClazz, Class<E> eClazz) {
        this.manager = manager;
        this.convert = convert;
        this.uploadResult = uploadResult;
        RepeatStrategyEnum repeatStrategyEnum = RepeatStrategyEnum.getInstance(uploadParam.getKeyStrategy());
        noRepeatCheck = repeatStrategyEnum == RepeatStrategyEnum.NONE;
        ignoreIfRepeat = repeatStrategyEnum == RepeatStrategyEnum.IGNORE;
        overrideIfRepeat = repeatStrategyEnum == RepeatStrategyEnum.OVERRIDE;
        breakOffIfRepeat = repeatStrategyEnum == RepeatStrategyEnum.BREAK_OFF;
        if (overrideIfRepeat) {
            cachedUpdateDataList = new ArrayList<>();
        }

        try {
            param = tClazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        if (ArrayUtils.isNotEmpty(uploadParam.getKeys())) {
            fieldPairList = new ArrayList<>(uploadParam.getKeys().length);
            for (String key : uploadParam.getKeys()) {
                try {
                    Field eClazzField = eClazz.getDeclaredField(key);
                    eClazzField.setAccessible(true);
                    Field paramClazzField = tClazz.getDeclaredField(key);
                    paramClazzField.setAccessible(true);
                    fieldPairList.add(new ImmutablePair<>(eClazzField, paramClazzField));
                } catch (NoSuchFieldException e) {
                    throw new RuntimeException(e);
                }
            }
        } else {
            fieldPairList = Collections.emptyList();
        }
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        ReadListener.super.onException(exception, context);
    }

    @Override
    public void invoke(E data, AnalysisContext context) {
        if (noRepeatCheck) {
            doInvoke(data, context);
            return;
        }

        T existsData = existData(data);
        if (existsData == null) {
            doInvoke(data, context);
            return;
        }
        if (ignoreIfRepeat) {
            return;
        }
        if (overrideIfRepeat) {
            convert.excelToEntityOverride(data, existsData);
            cachedUpdateDataList.add(existsData);
            return;
        }
        if (breakOffIfRepeat) {
            // todo 保存导入结果
            throw new ExcelAnalysisStopException();
        }
        doInvoke(data, context);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (overrideIfRepeat && CollectionUtils.isNotEmpty(cachedUpdateDataList)) {
            updateBatch(cachedUpdateDataList);
        }
    }

    protected T existData(E data) {
        if (CollectionUtils.isEmpty(fieldPairList)) {
            return null;
        }
        for (Pair<Field, Field> fieldFieldPair : fieldPairList) {
            try {
                Field tField = fieldFieldPair.getValue();
                Field eField = fieldFieldPair.getKey();
                tField.set(param, eField.get(data));
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        // 根据参数属性查询重复数据
        return manager.selectOneByExcelParam(param);
    }

    /**
     * 扫描excel的具体执行方法
     *
     * @param data    扫描数据
     * @param context 扫描excel上下文
     */
    protected abstract void doInvoke(E data, AnalysisContext context);

    /**
     * 批量更新
     *
     * @param updateDataList 待批量更新数据
     */
    protected abstract void updateBatch(List<T> updateDataList);
}
